package com.weige.elefence.utils;

import com.weige.elefence.annotation.*;
import com.weige.elefence.http.WebServer;
import com.weige.elefence.mq.Product;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ltj
 * @Description ....
 * @date 2022/5/23
 */
public class WebApplicationUtils {

    public static final Map<String, Class<?>> CLASS_MAP = new ConcurrentHashMap<>();
    public static final Map<String, Object> OBJ_MAP = new ConcurrentHashMap<>();
    public static final Map<String, Method> METHOD_MAP = new ConcurrentHashMap<>();

    private static final Logger log = LoggerFactory.getLogger(WebApplicationUtils.class);
    private Set<Class<?>> serviceClassSet;

    public static void run(Class<?> primarySource) {
        // 1.加载配置文件
        ReadJsonFileUtil.init();
        // 2.异步链接MQ
        ThreadPoolExecutorUtils.run(Product::run);
        // 3.初始化扫描
        ThreadPoolExecutorUtils.run(() -> new WebApplicationUtils().init(primarySource));
        // 4.启动Web服务
        ThreadPoolExecutorUtils.run(WebServer::run);
    }

    /**
     * 初始化扫描
     * @param primarySource:
     **/
    private void init(Class<?> primarySource) {
        // 获取类注解
        WebApplication webApplication = primarySource.getAnnotation(WebApplication.class);
        String[] strings = webApplication.scanBasePackages();
        String basePackage;
        // 包名
        if (strings.length == 0) {
            basePackage = primarySource.getPackage().getName();
        } else {
            basePackage = strings[0];
        }

        // 然后把我们的包名basePackage转换为路径名
        basePackage = basePackage.replace("*", "");
        // 获取扫描后全部class
        Reflections reflections = new Reflections(basePackage);
        // 获取带Service注解的类集合
        serviceClassSet = reflections.getTypesAnnotatedWith(Service.class);
        this.scanWebApi(reflections);
    }

    /**
     * 扫描类加载是否有webApi注解
     * @param reflections:
     **/
    private void scanWebApi(Reflections reflections) {
        Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(WebApi.class);
        try {
            for (Class<?> clazz : classSet) {
                WebApi webApi = clazz.getAnnotation(WebApi.class);
                String value = webApi.value();
                if (StringUtil.isEmpty(value)) {
                    value = clazz.getName();
                }
                boolean b = CLASS_MAP.containsKey(value);
                if (b) {
                    throw new RuntimeException("路由重复：" + value);
                }
                Method[] declaredMethods = clazz.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                    boolean annotationPresent = declaredMethod.isAnnotationPresent(WebRoute.class);
                    if (annotationPresent) {
                        WebRoute webRoute = declaredMethod.getAnnotation(WebRoute.class);
                        String url = webRoute.value();
                        METHOD_MAP.put(url, declaredMethod);
                    }
                }
                Object object = clazz.newInstance();
                this.scanAutowired(object);
                OBJ_MAP.put(value, object);
                CLASS_MAP.put(value, clazz);
                log.info("扫描路由：" + value);
            }
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描类成员变量自动注入
     * @param object:
     **/
    private void scanAutowired(Object object) {
        Field[] fields = object.getClass().getDeclaredFields();
        try {
            for (Field field : fields) {
                boolean isAutowired = field.isAnnotationPresent(Autowired.class);
                if (isAutowired) {

                    for (Class<?> clazz : serviceClassSet) {
                        if (clazz.getName().contains(field.getType().getName())) {
                            field.setAccessible(true);
                            field.set(object, clazz.newInstance());
                        }
                    }

                }
            }
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}
